﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System.Runtime.Serialization.Formatters.Soap;
using CDSBDemandControl.Model;
using CDSBDemandControlService.Translator;
using CDSBDemandControl.Configuration;
using System.IO;
using System.Diagnostics;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace CDSBDemandControlService
{
    public class DiscriminatorDemandService
    {
        string LockFilePath;
        string ServerName;
        string DemandManagerName;
        string PeriodStartDate;
        List<string> StatusUnderDevelopment;
        List<string> WorkItemsUnderDevelopment;
        List<string> WorkItemsDeveloped;
        List<DeveloperModel> Developers;
        List<string> TeamProjectsSinacor;
        List<string> TeamProjectsIMercado;
        List<string> TeamProjectsSiteBVMF;
        List<string> TeamProjectsDataStorage;
        List<string> StatusDeveloped;

        // IMPORTANTE: WIStore ficou como variável global devido a um problema conhecido no TFS de memory Leak.
        WorkItemStore WIStore;
        TeamFoundationServer tfs;

        public DiscriminatorDemandService() 
        {
            try
            {
                this.LoadConfigurations();
                this.WIStore = this.GetWorkItemStore(ServerName, ConfigManager.GetTFSUserAuthenticate());
            }
            catch (Exception err)
            {
                EventLog.WriteEntry("CDSBDemandControlService", err.StackTrace, EventLogEntryType.Error, 1);
            }
        }

        private void LoadConfigurations()
        {
            this.PeriodStartDate = ConfigManager.GetPeriodStartDate();
            this.StatusDeveloped = ConfigManager.GetStatusDeveloped();
            this.LockFilePath = ConfigManager.GetLockFilePath();
            this.ServerName = ConfigManager.GetServerName();
            this.DemandManagerName = ConfigManager.GetDemandManagerName();
            this.StatusUnderDevelopment = ConfigManager.GetStatusUnderDevelopment();
            this.WorkItemsUnderDevelopment = ConfigManager.GetWorkItemsUnderDevelopment();
            this.WorkItemsDeveloped = ConfigManager.GetWorkItemsDeveloped();
            this.Developers = (from Developer in ConfigManager.GetContributors() where Developer.Mission != Mission.FocalPoint select Developer).ToList<DeveloperModel>();
            this.TeamProjectsSinacor = ConfigManager.GetTeamProjectsSinacor();
            this.TeamProjectsIMercado = ConfigManager.GetTeamProjectsIMercado();
            this.TeamProjectsSiteBVMF = ConfigManager.GetTeamProjectsSiteBVMF();
            this.TeamProjectsDataStorage = ConfigManager.GetTeamProjectsDataStorage();
        }

        /// <summary>
        /// Executa uma WIQL(Work Item Query Language) e retorna uma DemandCollection
        /// </summary>
        /// <param name="wiql">string</param>
        /// <returns>DemandCollection</returns>
        public DemandCollection ExecuteQuery(string wiql, bool isScenario)
        {
            DemandCollection DCReturn = new DemandCollection();
            WorkItemCollection WorkitemsCollection = WIStore.Query(wiql);

            foreach (WorkItem item in WorkitemsCollection)
            {
                if (isScenario)
                    DCReturn.Add(XMLModelTranslate.TranslateScenario(item));
                else
                    DCReturn.Add(XMLModelTranslate.Translate(item));
            }

            WorkitemsCollection = null;

            return DCReturn;
        }

        public DemandCollection ListWorkItemsOutOfDate()
        {
            DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;

            #region Explicação sobre a Query
            // Seleciona os WorkItens que se enquadrem nos seguintes pré-requisitos:
            //  -> O Tipo de WorkItem esteja dentro da lista de workItems Atendidos;
            //  -> O Team Project deve estar dentro da lista de Team Projects Atendidos;
            //  -> A Disciplina seja Desenvolvimento
            //  -> O status deve estar dentro da lista de status considerados "Entregue"
            //  -> Referente às datas:
            //      -> O Término Realizado deve ser maior ou igual à data de inicio do Período
            //      -> Ou o Término Realizado está vazio e a data de data de criação é maior igual à 01/09/2009, Data base de início do Sistema;
            //      -> Jamais alterar a data base de inicio"01/09/2009".
            #endregion
            WiqlQuery = @"SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] in (" + WorkItemsDeveloped.ToInlineString() + @")"
                    + "and [System.TeamProject] in (" + TeamProjectsIMercado.ToInlineString() + "," + TeamProjectsSinacor.ToInlineString() + "," + TeamProjectsSiteBVMF.ToInlineString() + ")"
                    + "and [Microsoft.VSTS.Common.Discipline] = 'Desenvolvimento' and [System.State] in (" + StatusDeveloped.ToInlineString()
                    + ") and ([System.CreatedDate] >= '2009-09-01 00:00:00')";
                    
                    /*+ ") and ([Bovespa.Sinacor.TerminoRealizado] >= '" + PeriodStartDate + "' or ([Bovespa.Sinacor.TerminoRealizado] = '' and [System.CreatedDate] >= '2009-09-01 00:00:00'))";*/
                    

            // A sintaxe do WIQL não permite que se faça a comparação Microsoft.VSTS.Scheduling.FinishDate < Bovespa.Sinacor.TerminoRealizado
            // Então a verificação de quais tarefas realmente atrasaram é feita dentro do foreach abaixo
            foreach (XMLDemandModel svcdm in ExecuteQuery(WiqlQuery, false))
            {
                if (svcdm.EstimatedConclude.Date < svcdm.Conclude.Date || (svcdm.EstimatedConclude.Date < DateTime.Now.Date && svcdm.Conclude == DateTime.MinValue))
                    WICollection.Add(svcdm);
            }

            return WICollection;
        }

        public DemandCollection ListWorkItemsPending()
        {
            string WiqlQuery;

            #region Explicação sobre a Query
            // Seleciona os WorkItens que se enquadrem nos seguintes pré-requisitos:
            //  -> O Tipo de WorkItem esteja dentro da lista de workItems Atendidos;
            //  -> Que estejam designados a algum desenvolvedor ou Gestor de Demanda;
            //  -> Que sejam de team Projects atendidos pela área
            //  -> O status deve estar dentro da lista de status considerados "Em Desenvolvimento";
            //  -> Referente às datas:
            //      -> O Término Realizado deve ser maior ou igual à data de inicio do Período
            //      -> Ou o Término Realizado está vazio e a data de entrega estimada,ou data de criação é maior igual ao inicio do período 
            #endregion
            WiqlQuery = @"SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] in (" + WorkItemsUnderDevelopment.ToInlineString() + @")
                                    and [System.AssignedTo] in (" + Developers.ToInlineString() + ",'" + DemandManagerName + "')" +
                                    "and [System.TeamProject] in (" + TeamProjectsIMercado.ToInlineString() + "," +
                                                                     TeamProjectsSinacor.ToInlineString() + "," +
                                                                     TeamProjectsDataStorage.ToInlineString() + "," +
                                                                     TeamProjectsSiteBVMF.ToInlineString() + ")" +
                                    "and [System.State] in (" + StatusUnderDevelopment.ToInlineString() + @") ORDER BY [System.Id]";

            return ExecuteQuery(WiqlQuery, false);
        }

        public DemandCollection ListAllWorkItemsPeriod() 
        {
            //DemandCollection WICollection = new DemandCollection();

            string WiqlQuery;

            WiqlQuery = @"SELECT [System.Id] FROM WorkItems WHERE [System.WorkItemType] in (" + WorkItemsDeveloped.ToInlineString() + @")" +
                    "and [System.TeamProject] in (" + TeamProjectsIMercado.ToInlineString() + "," + TeamProjectsSinacor.ToInlineString() + "," + TeamProjectsSiteBVMF.ToInlineString() + ")"
                    + "and [Microsoft.VSTS.Common.Discipline] = 'Desenvolvimento' "
                    + "and ([System.CreatedDate] >= '2009-09-01 00:00:00')";
                    
                    /*+ "and ([Bovespa.Sinacor.TerminoRealizado] >= '" + PeriodStartDate + "' or ([Bovespa.Sinacor.TerminoRealizado] = '' and [System.CreatedDate] >= '2009-09-01 00:00:00'))";*/

            return this.ExecuteQuery(WiqlQuery,false);
        }
      
        public DemandCollection ListaGeracaoDisponibilidade()
        {
            //DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;
            string colaboradores = Developers.ToInlineString();

            
            WiqlQuery = @"SELECT [System.Id], [System.AreaPath], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title], 
                         [Microsoft.VSTS.Scheduling.StartDate], [Microsoft.VSTS.Scheduling.FinishDate]                         
                  FROM WorkItems 
                        WHERE 
                            [System.WorkItemType] IN ('TaskDesenv', 'Bug','BugInterno')  
                        AND [System.AssignedTo] IN 
                            (" + colaboradores + @")   
                        AND  [Bovespa.Sinacor.TerminoRealizado] = '' 
                        AND [System.CreatedDate] >= '2010-01-01 00:00:00' 
                        AND  [System.State] <> 'Fechada' 
                        AND  [System.State] <> 'Fechado'  
                        AND  [System.State] <> 'Cancelado'  
                        AND  [System.State] <> 'Cancelada' 
                        AND  [System.State] <> 'Integrado'  
                        AND  [System.State] <> 'Aprovado' 
                        ORDER BY [System.AssignedTo]";

            return this.ExecuteQuery(WiqlQuery, false);
        }
        
        public DemandCollection ListWorkItemsWhoReturnsCodeReview() 
        {
            //DemandCollection WICollection = new DemandCollection();

            string WiqlQuery = @"SELECT [System.Id] FROM WorkItems WHERE (State EVER 'Implementando Code Review') and ([System.TeamProject] <> 'Test' "
            + "and [Bovespa.Sinacor.TerminoRealizado] >= '" + PeriodStartDate + "' or ([Bovespa.Sinacor.TerminoRealizado] = '' and [System.CreatedDate] >= '2009-09-01 00:00:00'))";

            return this.ExecuteQuery(WiqlQuery, false);
        }

        public void CreateFileofWorkItems(string filePath, DemandCollection workItems)
        {
            MemoryStream memoryStream = new MemoryStream();
            //SoapFormatter SfDemand = new SoapFormatter();
            BinaryFormatter BFDemand = new BinaryFormatter();
            FileStream FSDemand;

            byte[] buffer;

            BFDemand.Serialize(memoryStream, workItems);

            memoryStream.Flush();

            buffer = memoryStream.GetBuffer();

            memoryStream.Close();
            memoryStream.Dispose();

            LockFile();

            try
            {
                FSDemand = File.Create(filePath);
                FSDemand.Write(buffer, 0, buffer.Length);
                FSDemand.Flush();
                FSDemand.Close();
                FSDemand.Dispose();
            }
            catch (Exception err)
            {
                EventLog.WriteEntry("CDSBDemandControlService", err.StackTrace, EventLogEntryType.Error, 1);
            }
            finally
            {
                UnLockFile();
            }

            buffer = null;
            BFDemand = null;
        }

        private void LockFile()
        {
            FileStream FSDemand = File.Create(LockFilePath);
            FSDemand.WriteByte(66);
            FSDemand.Flush();
            FSDemand.Close();
            FSDemand.Dispose();
        }

        private void UnLockFile()
        {
            FileStream FSDemand = File.Create(LockFilePath);
            FSDemand.WriteByte(65);
            FSDemand.Flush();
            FSDemand.Close();
            FSDemand.Dispose();
        }

        private WorkItemStore GetWorkItemStore(string serverName, CDSBNetWorkUser netWorkUser) 
        {
            CDSBNetworkCredentials Credentials = new CDSBNetworkCredentials(netWorkUser);

            tfs = TeamFoundationServerFactory.GetServer((serverName), Credentials);
            return (WorkItemStore)tfs.GetService(typeof(WorkItemStore));            
        }

        public DemandCollection GeraBugsLastMonth()
        {
            //DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;

            WiqlQuery = @"SELECT [System.Id], [System.AreaPath], [System.WorkItemType], [System.State], [System.AssignedTo], [System.Title],
                         [Microsoft.VSTS.Scheduling.StartDate], [Microsoft.VSTS.Scheduling.FinishDate]
                  FROM WorkItems
                        WHERE
                            [System.WorkItemType] = 'Bug'
                          AND [System.CreatedDate] >= " + "'" + PeriodStartDate + "'";

            return this.ExecuteQuery(WiqlQuery, false);
        }


        public DemandCollection GeraPrevisaoAlocacao()
        {
            //DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;

            WiqlQuery = @"SELECT    [System.Id], 
                                    [System.Title], 
                                    [Microsoft.VSTS.Scheduling.StartDate], 
                                    [Microsoft.VSTS.Scheduling.FinishDate], 
                                    [System.State] 
                        FROM WorkItems 
                                WHERE 
                                    [System.WorkItemType] = 'TaskDesenv'  
                                    AND  
                                    [System.AreaPath] NOT UNDER 'TEST' AND [Microsoft.VSTS.Scheduling.StartDate] >= " + "'" + new DateTime(DateTime.Now.Year, DateTime.Now.AddMonths(-1).Month, 1).Date + "'" + " ORDER BY [System.State]";

            return this.ExecuteQuery(WiqlQuery, false);
        }


        public DemandCollection GeraSolicitacoesSites()
        {
            //DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;

            WiqlQuery =     @"SELECT [System.Id] 
                            FROM WorkItems 
                            WHERE [System.WorkItemType] = 'Scenario' and " +
                            "[System.TeamProject] in (" + TeamProjectsSiteBVMF.ToInlineString() + ")";

            return this.ExecuteQuery(WiqlQuery, true);
        }

        public DemandCollection GeraManagementTeamTasks()
        {
            //DemandCollection WICollection = new DemandCollection();
            string WiqlQuery;
            List<DeveloperModel> lst = ConfigManager.GetContributors().ToList().FindAll(delegate(DeveloperModel dev) { return dev.Mission != Mission.Developer; }).ToList();

            WiqlQuery = @"

            SELECT [System.Id]
                FROM WorkItems WHERE [System.WorkItemType] IN 
                    ('TaskDesenv', 'Bug', 'BugInterno')  
            AND   [System.AssignedTo] IN (" + lst.ToInlineString() + @")  
            AND ( 
                  [System.State] <> 'Concluida'  
            AND   [System.State] <> 'Cancelado'              
            AND   [System.State] <> 'Cancelada'  
            AND   [System.State] <> 'Integrado' 
            AND   [System.State] <> 'Encerrado' 
                )                         
            AND   [System.AreaPath] NOT UNDER 'TEST'
            AND ([Microsoft.VSTS.Scheduling.StartDate] >= " + "'" + new DateTime(2010, 3, 1).Date + "' OR [Microsoft.VSTS.Scheduling.StartDate] = '')" + @"
            AND [System.CreatedDate] >= " + "'" + new DateTime(2010, 2, 1).Date + "'";            
            
            return this.ExecuteQuery(WiqlQuery, false);
        }

        public List<CDSBDemandControl.Model.ScenarioModel> ListScenariosAndTasks()
        {
            WorkItemCollection col;

            string WiqlQuery = @"SELECT [System.Id]
                                    FROM WorkItems 
                                        WHERE 
                                    [System.WorkItemType] = 'Scenario'  
                                        AND
                                    [System.AreaPath] NOT UNDER 'PSE'  
                                        AND  
                                    [System.AreaPath] NOT UNDER 'SinaINFR'  
                                        AND  
                                    [System.AreaPath] NOT UNDER 'TEST'
                                        AND  
                                    (
                                    [System.State] = 'Em Desenvolvimento'  
                                        OR  
                                    [System.State] = 'Aprovado para Desenvolvimento' 
                                    )
                                ORDER BY [System.Id]";

            col = WIStore.Query(WiqlQuery);
            List<ScenarioModel> scenarios = new List<ScenarioModel>();

            string related = string.Empty;

            foreach (WorkItem item in col)
            {
                string relatedInterno = string.Empty;
                foreach (Link lnk in item.Links)
                {
                    if (lnk.BaseType == BaseLinkType.RelatedLink)
                    {
                        if (related != string.Empty)
                            related += " , ";

                        if (relatedInterno != string.Empty)
                            relatedInterno += " , ";

                        related += ((Microsoft.TeamFoundation.WorkItemTracking.Client.RelatedLink)(lnk)).RelatedWorkItemId;
                        relatedInterno += ((Microsoft.TeamFoundation.WorkItemTracking.Client.RelatedLink)(lnk)).RelatedWorkItemId;
                    }
                }
                scenarios.Add(XMLModelTranslate.TranslateScenario(item, relatedInterno));
            }

            WiqlQuery = @"SELECT [System.Id]
                            FROM WorkItems 
                                WHERE 
                            [System.Id] IN (" + related + ") AND [System.WorkItemType] = 'TaskDesenv' ORDER BY [System.Id]";

            col = WIStore.Query(WiqlQuery);

            foreach (WorkItem item in col)
            {
                foreach (ScenarioModel s in scenarios)
                {
                    string[] rel = s.Related.Split(',');
                    foreach (string str in rel)
                    {
                        if (!string.IsNullOrEmpty(str))
                        {
                            if (str.Trim() == item.Id.ToString().Trim())
                                XMLModelTranslate.AddTaskDesenvToScenario(s, item);
                        }
                    }
                }
            }

            col = null;
            return scenarios;
        }

        public void CreateFileofWorkItems(string filePath, List<ScenarioModel> workItems)
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter BFDemand = new BinaryFormatter();
            FileStream FSDemand;

            byte[] buffer;

            BFDemand.Serialize(memoryStream, workItems);

            memoryStream.Flush();

            buffer = memoryStream.GetBuffer();

            memoryStream.Close();
            memoryStream.Dispose();

            LockFile();

            try
            {
                FSDemand = File.Create(filePath);
                FSDemand.Write(buffer, 0, buffer.Length);
                FSDemand.Flush();
                FSDemand.Close();
                FSDemand.Dispose();
            }
            catch (Exception err)
            {
                EventLog.WriteEntry("CDSBDemandControlService", err.StackTrace, EventLogEntryType.Error, 1);
            }
            finally
            {
                UnLockFile();
            }

            buffer = null;
            BFDemand = null;
        }

        #region Disposing

        public void Disposing()
        {
            WIStore = null;
            tfs.Dispose();
        }

        #endregion
    }
}
